import numpy as np
import matplotlib.pyplot as plt
import math

def cnvs_granularity_test_corrected(I_G, I_0, lambda_factor=2, rho_c=0.01, trials=3000):
    """
    Corrected Scalability test by reducing I_0 and increasing I_G.
    Enforces physical sampling (Coupon Collector) and integer lambda.
    """
    if not isinstance(lambda_factor, int) or lambda_factor < 1:
        raise ValueError("ERROR: lambda_factor must be an integer >= 1")

    # Calculation of required critical fragments (Rounded up)
    M_fragments = math.ceil(I_G / I_0)
    
    # Creation of the physical universe of nodes (exact copies)
    pool = np.repeat(np.arange(M_fragments), lambda_factor)
    N_total = len(pool)
    
    q_values = np.linspace(0.01, 0.99, 60)
    p_success = []

    for q in q_values:
        wins = 0
        k_captured = int(round(q * N_total))
        
        for _ in range(trials):
            # 1. Primary Attack: Physical capture without replacement
            captured_nodes = np.random.choice(pool, size=k_captured, replace=False)
            
            # Semantic knowledge: how many UNIQUE fragments were captured?
            unique_captured = len(np.unique(captured_nodes))
            
            # Missing fragments required for the Global Veto
            missing_fragments = M_fragments - unique_captured
            
            # 2. Secondary Attack: Inference/Dependency
            # Inference acts only on the fragments the attacker does NOT physically have
            p_inference = 1.0 - (1.0 - rho_c)**unique_captured
            
            inferred_fragments = 0
            if missing_fragments > 0:
                inferred_fragments = np.random.binomial(missing_fragments, p_inference)
            
            total_known = unique_captured + inferred_fragments
            
            # 3. Winning Condition (Global Veto strict enforcement)
            if total_known >= M_fragments:
                wins += 1
                
        p_success.append(wins / trials)
        
    return q_values, p_success, M_fragments

# ============================================================
# EXECUTION OF GRANULARITY SCENARIOS
# ============================================================
plt.figure(figsize=(11, 7))

np.random.seed(20260525)

# Scenarios: We progressively increase I_G and reduce I_0
scenarios = [
    {"I_G": 100,  "I_0": 10, "color": "green"},  # Low granularity (M = 10)
    {"I_G": 500,  "I_0": 5,  "color": "blue"},   # Medium granularity (M = 100)
    {"I_G": 1000, "I_0": 2,  "color": "orange"}, # High granularity (M = 500)
    {"I_G": 2000, "I_0": 1,  "color": "red"}     # Extreme granularity (M = 2000)
]

print("Running Corrected Granularity Test (Trials=3000)...")

# Parametri base per la scalatura semantica
base_rho_c = 0.01
base_I_0 = 10

for sc in scenarios:

    # CORREZIONE ENTROPICA:
    # la densità inferenziale diminuisce con la polverizzazione
    rho_effective = base_rho_c * (sc["I_0"] / base_I_0)

    q_vals, p_vals, M_calc = cnvs_granularity_test_corrected(
        I_G=sc["I_G"],
        I_0=sc["I_0"],
        lambda_factor=2,
        rho_c=rho_effective,
        trials=3000
    )
    plt.plot(
        q_vals, p_vals, 
        color=sc["color"], 
        linewidth=2.5, 
        label=f'I_G={sc["I_G"]}, I_0={sc["I_0"]} (M={M_calc})'
    )

# BFT Reference Line
plt.axvline(x=1/3, color='black', linestyle='--', alpha=0.7, label='BFT Limit (1/3)')

plt.title("CNVS Granularity Test: Effect of Data Pulverization (Integer \u03BB=2)", fontsize=14, fontweight='bold')
plt.xlabel("Fraction of Physically Corrupted Nodes (q)", fontsize=12)
plt.ylabel("Attacker Win Probability (False Positive)", fontsize=12)
plt.legend(loc='upper left', fontsize=11)
plt.grid(True, linestyle=':', alpha=0.7)

plt.tight_layout()
plt.savefig("CNVS_Test_2_Granularity_Corrected.png", dpi=200)
plt.show()

print("Execution complete. Ready for analysis.")